package com.ynet.middleground.approve.model;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import org.activiti.engine.*;
import org.activiti.engine.history.*;
import org.activiti.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ExecutionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.dozer.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Maps;
import com.ynet.core.common.BaseReqObj;
import com.ynet.core.common.PagerInfo;
import com.ynet.core.exception.BusinessException;
import com.ynet.core.log.IfpLogger;
import com.ynet.middleground.approve.bean.*;
import com.ynet.middleground.approve.cmd.DeleteTaskCmd;
import com.ynet.middleground.approve.cmd.SetFlowNodeAndGoCmd;
import com.ynet.middleground.approve.constant.OperatingTypeEnum;
import com.ynet.middleground.approve.constant.VotingVariables;
import com.ynet.middleground.approve.constant.WorkflowConstants;
import com.ynet.middleground.approve.dao.ArCommentExtMapper;
import com.ynet.middleground.approve.dao.ArTaskExtMapper;
import com.ynet.middleground.approve.dao.ArTaskOperateStageMapper;
import com.ynet.middleground.approve.dao.QueryTaskMapper;
import com.ynet.middleground.approve.dto.*;
import com.ynet.middleground.approve.entity.ArCommentExt;
import com.ynet.middleground.approve.entity.ArTaskExt;
import com.ynet.middleground.approve.entity.ArTaskOperateStage;
import com.ynet.middleground.approve.utils.BeanUtil;
import com.ynet.middleground.approve.utils.CommUtils;
import com.ynet.middleground.user.dto.UserBaseInformationDto;
import com.ynet.middleground.user.dto.UserInfoDto;

/**
 * @author liulx
 * @description 流程任务业务处理
 * @date 2019-10-16 13:08
 */
@Component
public class ProcessTaskModel {

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ManagementService managementService;

    @Autowired
    private Mapper dozerBeanMapper;

    @Autowired
    private ProcessRuntimeModel processRuntimeModel;

    @Autowired
    private UserCenterModel userCenterModel;

    @Autowired
    private TaskAttrModel taskAttrModel;

    @Autowired
    private QueryTaskMapper queryTaskMapper;

    @Autowired
    private ArTaskExtMapper arTaskExtMapper;

    @Autowired
    private ArTaskOperateStageMapper arTaskOperateStageMapper;

    @Autowired
    private ArCommentExtMapper arCommentExtMapper;

    @Autowired
    private MobileApproveModel mobileApproveModel;

    /**
     * 按照流程模糊查询
     */
    public static final String FLOW_LIKE_FLAG = "%";

    /**
     * 任务转办的备注类型，其它的任务操作备注类型默认为comment
     */
    public static final String TRANSFER_TYPE = "transfer";

    /**
     * 根据流程实例ID，查询当前活动任务 <br />
     * 如果是并行任务（比如会签），可能会有多个，所以用集合接受
     *
     * @param processInfoReq 任务处理请求对象: {流程实例ID}
     * @return 当前任务活动信息
     */
    public List<TaskDTO> getActiveTaskByInstanceId(ProcessInstanceReq processInfoReq) {
        if (StringUtils.isBlank(processInfoReq.getProcessInstanceId())) {
            throw new BusinessException("流程实例ID不能为空", "ECAP0001");
        }
        // 判断流程是否已经结束，如果已经结束，则没有活动任务
        ProcessInstance finishProcess = processRuntimeModel.getProcessInstance(processInfoReq.getProcessInstanceId());
        if (finishProcess == null) {
            throw new BusinessException("流程实例信息不存在或已结束", "ECAP0204");
        }
        List<Task> list = taskService.createTaskQuery().processInstanceId(processInfoReq.getProcessInstanceId())
            .active().includeProcessVariables().includeTaskLocalVariables().list();
        return list.stream().map(t -> {
            FlowNode taskFlowNode = getFlowNode(t.getProcessDefinitionId(), t.getTaskDefinitionKey());
            TaskDTO taskDTO = dozerBeanMapper.map(t, TaskDTO.class);
            taskDTO.setProcessVariables(t.getProcessVariables());
            Map<String, Object> taskLocalVariables = t.getTaskLocalVariables();
            taskDTO.setVariables(taskLocalVariables);

            Map<String, Object> localVariables = t.getTaskLocalVariables();
            if (localVariables != null && localVariables.size() > 0) {
                // 操作结果
                if (localVariables.get(WorkflowConstants.USER_TASK_APPROVE_RESULT) != null) {
                    taskDTO.setApproveResult(
                        String.valueOf(localVariables.get(WorkflowConstants.USER_TASK_APPROVE_RESULT)));
                }
                String votingNumber = (String)localVariables.get(VotingVariables.VOTING_FLAG);
                if (StringUtils.isNotBlank(votingNumber)) {
                    // 会签号
                    taskDTO.setCounterSignNumber(votingNumber);
                    taskDTO.setCountersign(true);
                } else {
                    taskDTO.setCountersign(false);
                }
            }

            Map<String, Object> processVariables = t.getProcessVariables();
            if (processVariables != null && processVariables.size() > 0) {
                // 发起人ID
                taskDTO.setStartUserId((String)processVariables.get(WorkflowConstants.START_USER_ID));
                // 发起渠道
                taskDTO.setStartChannelSignal((String)processVariables.get(WorkflowConstants.CHANNEL_SIGNAL));
                // 发起时间
                taskDTO.setStartTime(String.valueOf(processVariables.get(WorkflowConstants.FLOW_START_TIME)));
            }

            // 封装业务动作参数集合
            setBusinessOperateData(taskDTO, taskLocalVariables);

            UserTask userTaskDefinition;
            if (taskFlowNode instanceof UserTask) {
                userTaskDefinition = (UserTask)taskFlowNode;
                taskDTO.setCandidateUsers(userTaskDefinition.getCandidateUsers());
                taskDTO.setCandidateGroups(userTaskDefinition.getCandidateGroups());
                taskDTO.setCountersign(userTaskDefinition.hasMultiInstanceLoopCharacteristics());
            }
            return taskDTO;
        }).collect(Collectors.toList());
    }

    /**
     * 查询个人任务，个人任务+未签收任务 <br />
     *
     *
     * @param queryTaskReqPager 分页查询任务列表请求对象: {处理人,候选用户,候选用户组, 查询类型, 开始查询日期, 结束查询日期}
     */
    public List<TaskDTO> listTodoTaskByUserId(QueryTaskReqPager queryTaskReqPager) {
        // 创建任务查询对象
        TaskQuery taskQuery = getTaskQueryWithChannel(queryTaskReqPager);
        // 获取参数
        PagerInfo pagerInfo = queryTaskReqPager.getPagerInfo();
        String assignee = queryTaskReqPager.getAssignee();

        // 情况1： 属于个人的任务列表，包括已经签收
        taskQuery.taskAssignee(assignee).active().includeProcessVariables().orderByTaskCreateTime().desc();

        // 设置查询条件
        setQueryCondition(queryTaskReqPager, taskQuery);
        int start = (pagerInfo.getPageNum() - 1) * pagerInfo.getPageSize();
        List<Task> todoTasks = taskQuery.listPage(start, pagerInfo.getPageSize());
        long todoCount = taskQuery.count();
        List<TaskDTO> todoResult = BeanUtil.mapList(dozerBeanMapper, todoTasks, TaskDTO.class);

        // 情况2： 未签收的任务列表
        taskQuery = getTaskQueryWithChannel(queryTaskReqPager);
        taskQuery =
            taskQuery.taskCandidateUser(assignee).includeProcessVariables().active().orderByTaskCreateTime().desc();
        // 设置查询条件
        setQueryCondition(queryTaskReqPager, taskQuery);
        List<Task> toClaimTasks = taskQuery.listPage(start, pagerInfo.getPageSize());
        long toClaimCount = taskQuery.count();
        List<TaskDTO> toClaimResult = BeanUtil.mapList(dozerBeanMapper, toClaimTasks, TaskDTO.class);
        // 合并两个情况
        int sub = pagerInfo.getPageSize() - todoResult.size();
        List<TaskDTO> subTaskList = toClaimResult.stream().limit(sub).collect(Collectors.toList());
        todoResult.addAll(subTaskList);
        pagerInfo.setTotal(toClaimCount + todoCount);
        return todoResult;
    }

    /**
     * 设置任务查询条件
     *
     * @param queryTaskReqPager 任务分页请求对象
     * @param taskQuery 查询对象
     */
    private void setQueryCondition(QueryTaskReqPager queryTaskReqPager, TaskQuery taskQuery) {
        if (queryTaskReqPager.getBeginDate() != null) {
            taskQuery.taskCreatedAfter(queryTaskReqPager.getBeginDate());
        }
        if (queryTaskReqPager.getEndDate() != null) {
            taskQuery.taskCreatedBefore(queryTaskReqPager.getEndDate());
        }
    }

    /**
     * 查询组任务 <br />
     *
     *
     * @param queryTaskReqPager 用户组列表
     */
    public List<TaskDTO> listTodoTaskByGroupIds(QueryTaskReqPager queryTaskReqPager) {
        List<String> groupIds = queryTaskReqPager.getCandidateGroups();
        TaskQuery taskQuery = taskService.createTaskQuery();
        List<Task> tasks = taskQuery.taskCandidateGroupIn(groupIds).list();
        return BeanUtil.mapList(dozerBeanMapper, tasks, TaskDTO.class);
    }

    /**
     * 获取任务查询对象，并指定好渠道
     *
     * @param reqObj 基础请求类
     * @return 任务查询对象
     */
    public TaskQuery getTaskQueryWithChannel(BaseReqObj reqObj) {
        // 创建任务查询对象
        TaskQuery taskQuery = taskService.createTaskQuery();

        // Cancel By Lee On 2021-01-26 Start
        // 需要在启动流程参数中添加渠道标示，用于区别用户是在哪个渠道查询待办任务
        // taskQuery.processVariableValueEquals(WorkflowConstants.CHANNEL_SIGNAL, reqObj.getChannel());
        // Cancel By Lee On 2021-01-26 End

        // Add By Lee On 2021-01-26 Start
        // 跨渠道审批相关
        // 据上送的渠道，过滤属于“可操作渠道”内的数据；
        taskQuery.processVariableValueLike(WorkflowConstants.FLOW_CAN_OPERATE_CHANNEL_SIGNAL,
            FLOW_LIKE_FLAG + reqObj.getChannel() + FLOW_LIKE_FLAG);
        // Add By Lee On 2021-01-26 End
        return taskQuery;
    }

    /**
     * 分页查询用户待审批任务列表 <br>
     * 如果任务assignee字段为空，则需要签收
     *
     * @param queryTaskReqPager 分页查询任务列表请求对象: {处理人,候选用户组, 查询类型, 开始查询日期, 结束查询日期}
     * @return List<TaskDTO> 待审批任务列表
     *
     */
    public List<TaskDTO> listAuditingTaskByPage(QueryTaskReqPager queryTaskReqPager) {
        // 创建任务查询对象
        TaskQuery taskQuery = getTaskQueryWithChannel(queryTaskReqPager);
        // Add By Lee On 2022-09-19 Start
        // 业态相关
        if (!StringUtils.isEmpty(queryTaskReqPager.getCurrentFormatCode())) {
            taskQuery.processVariableValueLike(WorkflowConstants.FLOW_CAN_OPERATE_FORMAT_SIGNAL,
                FLOW_LIKE_FLAG + queryTaskReqPager.getCurrentFormatCode() + FLOW_LIKE_FLAG);
        }
        // Add Lee On 2022-09-19 End
        // 获取参数
        PagerInfo pagerInfo = queryTaskReqPager.getPagerInfo();
        String assignee = queryTaskReqPager.getAssignee();
        List<String> groupIds = queryTaskReqPager.getCandidateGroups();
        // 查询个人身份和候选人身份的任务
        // 添加任务处理人条件
        if (StringUtils.isNotBlank(assignee)) {
            taskQuery.taskCandidateOrAssigned(assignee);
        }

        setQueryCondition(queryTaskReqPager, taskQuery);
        taskQuery.orderByTaskCreateTime().desc();
        // 计算应该从第几条记录去取
        int start = (pagerInfo.getPageNum() - 1) * pagerInfo.getPageSize();
        List<Task> tasks = taskQuery.listPage(start, pagerInfo.getPageSize());
        // 获取总数
        long todoCount = taskQuery.count();
        List<TaskDTO> todoTaskList = BeanUtil.mapList(dozerBeanMapper, tasks, TaskDTO.class);
        todoTaskList.forEach(task -> {
            Map<String, Object> variables = taskService.getVariables(task.getId());
            task.setVariables(variables);
        });

        // 查询组任务, 如果不传组或传空，说明没有查询条件，默认使用用户所拥有的部门、角色等信息
        if (groupIds == null || groupIds.isEmpty()) {
            groupIds = userCenterModel.listUserGroupsByUserId(assignee, queryTaskReqPager.getChannel(),
                queryTaskReqPager.getOperationUserId(), queryTaskReqPager.getOrganId());
        }

        // 如果查询用户关系，组信息仍然为空，则不执行组查询
        if (groupIds == null || groupIds.isEmpty()) {
            pagerInfo.setTotal(todoCount);
            return todoTaskList;
        }

        taskQuery = getTaskQueryWithChannel(queryTaskReqPager);

        // Add By Lee On 2022-09-19 Start
        // 业态相关
        if (!StringUtils.isEmpty(queryTaskReqPager.getCurrentFormatCode())) {
            taskQuery.processVariableValueLike(WorkflowConstants.FLOW_CAN_OPERATE_FORMAT_SIGNAL,
                FLOW_LIKE_FLAG + queryTaskReqPager.getCurrentFormatCode() + FLOW_LIKE_FLAG);
        }
        // Add Lee On 2022-09-19 End

        setQueryCondition(queryTaskReqPager, taskQuery);
        taskQuery.orderByTaskCreateTime().desc();
        List<Task> groupTasks = taskQuery.taskCandidateGroupIn(groupIds).listPage(start, pagerInfo.getPageSize());
        long groupTodoCount = taskQuery.count();
        List<TaskDTO> groupTasksList = BeanUtil.mapList(dozerBeanMapper, groupTasks, TaskDTO.class);
        groupTasksList.forEach(task -> {
            Map<String, Object> variables = taskService.getVariables(task.getId());
            task.setVariables(variables);
        });
        // 合并两个情况，如果个人任务不够pageSize字段的值，，会从组任务补全
        int sub = pagerInfo.getPageSize() - todoTaskList.size();
        List<TaskDTO> subTaskList = groupTasksList.stream().limit(sub).collect(Collectors.toList());
        todoTaskList.addAll(subTaskList);

        // 移动审批改造时，增加此返回项
        todoTaskList.forEach(task -> {
            // 是否已读标记
            LambdaQueryWrapper<ArTaskExt> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(ArTaskExt::getTaskId, task.getId());
            queryWrapper.eq(ArTaskExt::getUserId, queryTaskReqPager.getAssignee());
            List<ArTaskExt> arTaskExts = arTaskExtMapper.selectList(queryWrapper);
            if (arTaskExts.size() > 0) {
                task.setIsReadFlag(true);
            }
            if (arTaskExts.size() == 0) {
                task.setIsReadFlag(false);
            }
        });

        pagerInfo.setTotal(todoCount + groupTodoCount);
        return todoTaskList;
    }

    /**
     * 分页查询用户待审批任务列表(移动端专用)
     * 
     * @param queryTaskReqPager
     * @return
     * @Author liwq
     */
    public List<TaskDTOForMobile> listAuditingTaskByPageForMobile(QueryTaskForMobileReqPager queryTaskReqPager,
        PagerInfoCount pagerInfoCount) {
        // Step1: 准备参数
        // 处理人
        String assignee = queryTaskReqPager.getAssignee();
        // 渠道
        String channel = queryTaskReqPager.getChannel();
        // 模糊查询条件
        String condition = queryTaskReqPager.getCondition();
        if (!StringUtils.isEmpty(condition)) {
            queryTaskReqPager.setCondition("%" + queryTaskReqPager.getCondition() + "%");
        }
        // 分页字段
        PagerInfo pagerInfo = queryTaskReqPager.getPagerInfo();
        Integer startIndex = 0;
        Integer pageNum = pagerInfo.getPageNum();
        Integer pageSize = pagerInfo.getPageSize();
        if (pageNum != null && pageSize != null) {
            startIndex = (pageNum - 1) * pageSize;
        }

        // 组任务, 如果不传组或传空，说明没有查询条件，默认使用用户所拥有的部门、角色等信息
        List<String> groupIds = queryTaskReqPager.getCandidateGroups();
        if (groupIds == null || groupIds.isEmpty()) {
            groupIds = userCenterModel.listUserGroupsByUserId(assignee, channel, queryTaskReqPager.getOperationUserId(),
                queryTaskReqPager.getOrganId());
        }

        // Step2: 分页查询待办任务列表
        List<TaskDTOForMobile> list =
            queryTaskMapper.listAuditingTaskByPageForMobile(queryTaskReqPager, groupIds, pageSize, startIndex);
        // 总数
        final Integer total = queryTaskMapper.countTotal(queryTaskReqPager, groupIds);
        dozerBeanMapper.map(pagerInfo, pagerInfoCount);
        pagerInfoCount.setToDoTaskCount(total);
        // Step3: 补充一些参数，例如：任务变量等,并返回
        list.forEach(task -> {
            // 任务变量
            Map<String, Object> localVariables = taskService.getVariablesLocal(task.getId());
            task.setVariables(localVariables);

            // 流程变量
            Map<String, Object> processVariables = null;
            HistoricProcessInstance process = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(task.getProcessInstanceId()).includeProcessVariables().singleResult();
            if (process != null) {
                processVariables = process.getProcessVariables();
            }
            task.setProcessVariables(processVariables);

            // 流程发起时间
            // 优先取业务发起时间
            String startDate =
                task.getStartDate() != null ? task.getStartDate() : String.valueOf(process.getStartTime().getTime());
            task.setStartDate(startDate);

            // 是否等待指令
            LambdaQueryWrapper<ArTaskOperateStage> query = new LambdaQueryWrapper<>();
            query.eq(ArTaskOperateStage::getTaskId, task.getId());
            query.eq(ArTaskOperateStage::getAssignee, queryTaskReqPager.getAssignee());
            ArTaskOperateStage arTaskOperateStage = arTaskOperateStageMapper.selectOne(query);

            // 是否处理中
            task.setIsWait(
                (arTaskOperateStage != null && arTaskOperateStage.getIsAllow() == null) || (arTaskOperateStage != null
                    && arTaskOperateStage.getIsAllow() && arTaskOperateStage.getIsToBeSigned()));

            // 任务评论
            List<Comment> taskComments = taskService.getTaskComments(task.getId());
            List<CommentDTO> taskCommentsDTO = BeanUtil.mapList(dozerBeanMapper, taskComments, CommentDTO.class);
            task.setTaskComments(taskCommentsDTO);

            // 流程评论
            List<Comment> processComments = taskService.getProcessInstanceComments(task.getProcessInstanceId());
            List<CommentDTO> processCommentsDTO = BeanUtil.mapList(dozerBeanMapper, processComments, CommentDTO.class);
            task.setProcessComments(processCommentsDTO);

            // 业务动作提示
            setBusinessOperateData(task, task.getVariables());

            // 是否已读标记
            LambdaQueryWrapper<ArTaskExt> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(ArTaskExt::getTaskId, task.getId());
            queryWrapper.eq(ArTaskExt::getUserId, queryTaskReqPager.getAssignee());
            List<ArTaskExt> arTaskExts = arTaskExtMapper.selectList(queryWrapper);
            if (arTaskExts.size() > 0) {
                task.setIsReadFlag(true);
            }
            if (arTaskExts.size() == 0) {
                task.setIsReadFlag(false);
            }
            // 钉钉ID
            List<UserInfoDto> userInfoDtos = userCenterModel.queryUserInfo(queryTaskReqPager.getAssignee(),
                queryTaskReqPager.getChannel(), String.valueOf(queryTaskReqPager.getOperationUserId()));
            if (userInfoDtos.size() > 0 && userInfoDtos.get(0).getUserBaseInformationDto() != null) {
                task.setDingtalkUserId(userInfoDtos.get(0).getUserBaseInformationDto().getDingtalkUserid());
            }
            // 待办数量总数
            task.setToDoTaskCount(total);

        });

        pagerInfo.setTotal(total);
        return list;
    }

    /**
     * 分页查询用户已审批任务列表
     *
     * @param queryTaskReqPager 分页查询任务列表请求对象: {处理人,候选用户,候选用户组, 查询类型}
     * @return List<TaskDTO> 已审批任务列表
     */
    public List<TaskDTO> listAuditedTaskByPage(QueryTaskReqPager queryTaskReqPager) {
        PagerInfo pagerInfo = queryTaskReqPager.getPagerInfo();
        String assignee = queryTaskReqPager.getAssignee();
        List<TaskDTO> result = Collections.emptyList();
        if (StringUtils.isBlank(assignee)) {
            throw new BusinessException("任务处理人不能为空", "ECAP0001");
        }
        HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
            // 需要在启动流程参数中添加渠道标示，用于区别用户是在哪个渠道查询待办任务
            // Cancel By Lee On 2021-01-26 Start
            // .processVariableValuecompleteTaskEquals(WorkflowConstants.CHANNEL_SIGNAL, queryTaskReqPager.getChannel())
            // Cancel By Lee On 2021-01-26 End
            .taskAssignee(assignee).finished().includeProcessVariables()
            // .includeTaskLocalVariables()
            .orderByHistoricTaskInstanceEndTime().desc();

        // Add By Lee On 2021-01-26 Start
        // 跨渠道审批相关
        // 据上送的渠道，过滤属于“可操作渠道completeTask”内的数据；
        historicTaskInstanceQuery.processVariableValueLike(WorkflowConstants.FLOW_CAN_OPERATE_CHANNEL_SIGNAL,
            FLOW_LIKE_FLAG + queryTaskReqPager.getChannel() + FLOW_LIKE_FLAG);
        // Add By Lee On 2021-01-26 End

        // Add By Lee On 2022-09-19 Start
        // 业态相关
        if (!StringUtils.isEmpty(queryTaskReqPager.getCurrentFormatCode())) {
            historicTaskInstanceQuery.processVariableValueLike(WorkflowConstants.FLOW_CAN_OPERATE_FORMAT_SIGNAL,
                FLOW_LIKE_FLAG + queryTaskReqPager.getCurrentFormatCode() + FLOW_LIKE_FLAG);
        }
        // Add Lee On 2022-09-19 End

        if (queryTaskReqPager.getBeginDate() != null) {
            historicTaskInstanceQuery.taskCompletedAfter(queryTaskReqPager.getBeginDate());
        }
        if (queryTaskReqPager.getEndDate() != null) {
            historicTaskInstanceQuery.taskCompletedBefore(queryTaskReqPager.getEndDate());
        }

        // 计算应该从第几条记录去取
        int start = (pagerInfo.getPageNum() - 1) * pagerInfo.getPageSize();
        List<HistoricTaskInstance> taskList = historicTaskInstanceQuery.listPage(start, pagerInfo.getPageSize());
        pagerInfo.setTotal(historicTaskInstanceQuery.count());
        result = taskList.stream().map(t -> {
            TaskDTO taskDTO = dozerBeanMapper.map(t, TaskDTO.class);
            taskDTO.setProcessVariables(t.getProcessVariables());

            // add by liwq on 2021-08-18 start
            // 获取任务变量（改在这里获取，因为这样获取速度更快）
            HistoricTaskInstance h = historyService.createHistoricTaskInstanceQuery().taskId(t.getId())
                .includeTaskLocalVariables().singleResult();
            taskDTO.setVariables(h.getTaskLocalVariables());
            // add by liwq on 2021-08-18 end

            // 是否已读标记
            // 移动审批改造时，增加此返回项
            LambdaQueryWrapper<ArTaskExt> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(ArTaskExt::getTaskId, t.getId());
            queryWrapper.eq(ArTaskExt::getUserId, queryTaskReqPager.getAssignee());
            List<ArTaskExt> arTaskExts = arTaskExtMapper.selectList(queryWrapper);
            if (arTaskExts.size() > 0) {
                taskDTO.setIsReadFlag(true);
            }
            if (arTaskExts.size() == 0) {
                taskDTO.setIsReadFlag(false);
            }
            return taskDTO;
        }).collect(Collectors.toList());
        return result;
    }

    /**
     * 分页查询用户已审批任务列表(移动端专用)
     *
     * @param queryTaskReqPager 分页查询任务列表请求对象: {处理人,候选用户,候选用户组, 查询类型}
     * @return List<TaskDTO> 已审批任务列表
     */
    public List<TaskDTOForMobile> listAuditedTaskByPageForMobile(QueryTaskForMobileReqPager queryTaskReqPager,
        PagerInfoCount pagerInfoCount) {
        // Step1: 参数准备
        PagerInfo pagerInfo = queryTaskReqPager.getPagerInfo();
        String assignee = queryTaskReqPager.getAssignee();
        List<TaskDTOForMobile> result = Collections.emptyList();
        if (StringUtils.isBlank(assignee)) {
            throw new BusinessException("任务处理人不能为空", "ECAP0001");
        }

        // Srep2: 准备查询条件
        HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
            // 需要在启动流程参数中添加渠道标示，用于区别用户是在哪个渠道查询待办任务
            .taskAssignee(assignee).finished().includeProcessVariables().includeTaskLocalVariables();

        // Add By liwq On 2021-01-26 Start
        // 跨渠道审批相关; 据上送的渠道，过滤属于“可操作渠道completeTask”内的数据；
        if (!StringUtils.isEmpty(queryTaskReqPager.getQueryChannel())) {
            historicTaskInstanceQuery.processVariableValueLike(WorkflowConstants.FLOW_CAN_OPERATE_CHANNEL_SIGNAL,
                FLOW_LIKE_FLAG + queryTaskReqPager.getQueryChannel() + FLOW_LIKE_FLAG);
        }
        // Add By liwq On 2021-01-26 End

        // add by liwq on 2023-03-13 start 此处为新增的根据业态ID查询条件
        if (!StringUtils.isEmpty(queryTaskReqPager.getFormat())) {
            historicTaskInstanceQuery.processVariableValueLike(WorkflowConstants.FORMAT_SIGNAL,
                queryTaskReqPager.getFormat());
        }
        // add by liwq on 2023-03-13 end

        // Add By liwq On 2021-03-17 Start
        // 移动审批相关，添加过滤条件
        // 1:已完成，自己已经审批完，并且整个流程也结束了；
        if (1 == queryTaskReqPager.getType()) {
            historicTaskInstanceQuery.processFinished();
        }
        // 2：在办，自己已经审批完，但是整个流程并未结束；
        if (2 == queryTaskReqPager.getType()) {
            historicTaskInstanceQuery.processUnfinished();
        }
        // 只过滤出需要在移动端展示的节点
        historicTaskInstanceQuery.taskVariableValueEquals(WorkflowConstants.FLOW_TASK_MOBILE_PROCESS, true);
        // 筛选条件
        if (!StringUtils.isEmpty(queryTaskReqPager.getCondition())) {
            queryTaskReqPager.setCondition("%" + queryTaskReqPager.getCondition() + "%");
            historicTaskInstanceQuery.or().processDefinitionNameLike(queryTaskReqPager.getCondition())
                .processVariableValueLike(WorkflowConstants.FLOW_COMPANY_NAME, queryTaskReqPager.getCondition())
                .processVariableValueLike(WorkflowConstants.FLOW_PRODUCT_NAME, queryTaskReqPager.getCondition())
                .processVariableValueLike(WorkflowConstants.FLOW_START_USER_NAME, queryTaskReqPager.getCondition())
                .processVariableValueLike(WorkflowConstants.FLOW_BUSINESS_QDPF_OPERATOR_NAME,
                    queryTaskReqPager.getCondition())
                .processVariableValueLike(WorkflowConstants.FLOW_PROCESS_INSTANCE_ID, queryTaskReqPager.getCondition())
                // add by liwq on 2023-03-13 start 此处为新增的根据流程摘要查询
                .processVariableValueLike(WorkflowConstants.FLOW_BUSINESS_SUMMARY, queryTaskReqPager.getCondition());
            // add by liwq on 2023-03-13 end 此处为新增的根据流程摘要查询

        }
        // 0:降序
        if (0 == queryTaskReqPager.getDateOrder()) {
            historicTaskInstanceQuery.orderByProcessInstanceId().desc();
        }
        // 1:升序
        if (1 == queryTaskReqPager.getDateOrder()) {
            historicTaskInstanceQuery.orderByProcessInstanceId().asc();
        }
        // Add By liwq On 2021-03-17 End

        if (queryTaskReqPager.getBeginDate() != null) {
            historicTaskInstanceQuery.processVariableValueGreaterThan(WorkflowConstants.FLOW_START_TIME,
                queryTaskReqPager.getBeginDate().getTime());
        }
        if (queryTaskReqPager.getEndDate() != null) {
            historicTaskInstanceQuery.processVariableValueLessThan(WorkflowConstants.FLOW_START_TIME,
                queryTaskReqPager.getEndDate().getTime());
        }
        // 计算应该从第几条记录去取
        int start = (pagerInfo.getPageNum() - 1) * pagerInfo.getPageSize();

        // Step3: 开始查询
        List<HistoricTaskInstance> taskList = historicTaskInstanceQuery.listPage(start, pagerInfo.getPageSize());
        pagerInfo.setTotal(historicTaskInstanceQuery.count());
        // 待办总数
        List<String> groupIds = queryTaskReqPager.getCandidateGroups();
        if (groupIds == null || groupIds.isEmpty()) {
            groupIds = userCenterModel.listUserGroupsByUserId(assignee, queryTaskReqPager.getChannel(),
                queryTaskReqPager.getOperationUserId(), queryTaskReqPager.getOrganId());
        }
        QueryTaskForMobileReqPager toDoQuery = new QueryTaskForMobileReqPager();
        toDoQuery.setAssignee(queryTaskReqPager.getAssignee());
        toDoQuery.setType(0);
        toDoQuery.setChannel(queryTaskReqPager.getChannel());
        toDoQuery.setTraceId(queryTaskReqPager.getTraceId());
        toDoQuery.setOperationUserId(queryTaskReqPager.getOperationUserId());
        toDoQuery.setRequestTime(queryTaskReqPager.getRequestTime());
        final Integer total = queryTaskMapper.countTotal(toDoQuery, groupIds);
        dozerBeanMapper.map(pagerInfo, pagerInfoCount);
        pagerInfoCount.setToDoTaskCount(total);
        // Step4: 整理并返回
        result = taskList.stream().map(t -> {
            TaskDTOForMobile taskDTO = dozerBeanMapper.map(t, TaskDTOForMobile.class);
            taskDTO.setProcessVariables(t.getProcessVariables());
            taskDTO.setVariables(t.getTaskLocalVariables());

            // 流程发起时间
            Map<String, Object> processVariables = null;
            HistoricProcessInstance process = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(t.getProcessInstanceId()).includeProcessVariables().singleResult();
            if (process != null) {
                processVariables = process.getProcessVariables();
                String startDate = String.valueOf(processVariables.get(WorkflowConstants.FLOW_START_TIME));
                taskDTO.setStartDate(startDate != null ? startDate : String.valueOf(process.getStartTime().getTime()));
            }

            // 任务评论
            List<Comment> taskComments = taskService.getTaskComments(t.getId());
            List<CommentDTO> taskCommentsDTO = BeanUtil.mapList(dozerBeanMapper, taskComments, CommentDTO.class);
            taskDTO.setTaskComments(taskCommentsDTO);

            // 流程评论
            List<Comment> processComments = taskService.getProcessInstanceComments(t.getProcessInstanceId());
            List<CommentDTO> processCommentsDTO = BeanUtil.mapList(dozerBeanMapper, processComments, CommentDTO.class);
            taskDTO.setProcessComments(processCommentsDTO);

            // 业务动作提示
            setBusinessOperateData(taskDTO, taskDTO.getVariables());

            // 是否已读标记
            LambdaQueryWrapper<ArTaskExt> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(ArTaskExt::getTaskId, t.getId());
            queryWrapper.eq(ArTaskExt::getUserId, queryTaskReqPager.getAssignee());
            List<ArTaskExt> arTaskExts = arTaskExtMapper.selectList(queryWrapper);
            if (arTaskExts.size() > 0) {
                taskDTO.setIsReadFlag(true);
            }
            if (arTaskExts.size() == 0) {
                taskDTO.setIsReadFlag(false);
            }
            // 钉钉ID
            List<UserInfoDto> userInfoDtos = userCenterModel.queryUserInfo(queryTaskReqPager.getAssignee(),
                queryTaskReqPager.getChannel(), String.valueOf(queryTaskReqPager.getOperationUserId()));
            if (userInfoDtos.size() > 0 && userInfoDtos.get(0).getUserBaseInformationDto() != null) {
                taskDTO.setDingtalkUserId(userInfoDtos.get(0).getUserBaseInformationDto().getDingtalkUserid());
            }
            // 待办任务总数
            taskDTO.setToDoTaskCount(total);
            return taskDTO;
        }).collect(Collectors.toList());
        return result;
    }

    /**
     * 任务转办
     *
     * @param transferTaskReq 任务处理请求对象: {taskId, assignee, receiver}
     */
    @Transactional(rollbackFor = Exception.class)
    public void transferTask(TransferTaskReq transferTaskReq) {
        // 待转办任务ID
        String taskId = transferTaskReq.getTaskId();
        // 任务现在处理人
        String assignee = transferTaskReq.getAssignee();
        // 任务转办接受人
        String receiver = transferTaskReq.getReceiver();
        if (StringUtils.isBlank(taskId)) {
            throw new BusinessException("taskId 任务ID不能为空", "ECAP0001");
        }
        if (StringUtils.isBlank(receiver)) {
            throw new BusinessException("receiver 任务接受人不能为空", "ECAP0001");
        }

        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task != null) {
            /*// 根据候选人和组任务id查询，如果有记录说明该 候选人有资格拾取该 任务
            Task task2 = taskService.createTaskQuery().taskId(taskId).taskCandidateUser(receiver).singleResult();
            if (task2 != null) {
                // 才可以交接
                taskService.setAssignee(taskId, receiver);
            }*/
            // 校验assignee是否是taskId的原负责人
            if (StringUtils.isNotBlank(assignee) && !assignee.equals(task.getAssignee())) {
                throw new BusinessException("任务处理人与原任务处理人不一致", "ECAP0401");
            }

            UserBaseInformationDto assigneeInfo = userCenterModel.getUserBaseInfoFirst(assignee,
                transferTaskReq.getChannel(), String.valueOf(transferTaskReq.getOperationUserId()));

            UserBaseInformationDto receiverInfo = userCenterModel.getUserBaseInfoFirst(receiver,
                transferTaskReq.getChannel(), String.valueOf(transferTaskReq.getOperationUserId()));

            ArCommentExt commentExt = new ArCommentExt();

            commentExt.setTaskId(taskId);
            commentExt.setProcInstId(task.getProcessInstanceId());
            commentExt.setOperateType(String.valueOf(OperatingTypeEnum.TRANSFER.getCode()));
            commentExt.setAssignee(Integer.valueOf(assignee));
            commentExt.setAssigneeName(assigneeInfo.getUserName());
            commentExt.setToAssignee(Integer.valueOf(receiver));
            commentExt.setToAssigneeName(receiverInfo.getUserName());
            List<CommentFile> commentFiles = transferTaskReq.getCommentFiles();
            commentExt.setFiles(commentFiles != null ? JSON.toJSONString(commentFiles) : null);
            commentExt.setIsDeleted(0);
            commentExt.setGmtCreate(LocalDateTime.now());
            commentExt.setCreateBy(transferTaskReq.getOperationUserId());
            commentExt.setGmtModified(LocalDateTime.now());
            commentExt.setModifiedBy(transferTaskReq.getOperationUserId());

            // 更改任务处理人为接受人
            taskService.setAssignee(taskId, receiver);

            // add by liwq on 2021-03-01 start
            // 添加转办备注，"transfer"是转办的备注类型，其它的任务备注类型默认comment
            if (StringUtils.isNotEmpty(task.getAssignee())) {
                Comment comment = taskService.addComment(taskId, task.getProcessInstanceId(), TRANSFER_TYPE,
                    task.getAssignee() + ",转办至," + receiver);
                // 设置 commentId 关联
                commentExt.setCommentId(comment.getId());
            }
            // 落库记录
            arCommentExtMapper.insert(commentExt);
            // add by liwq on 2021-03-01 end
        } else {
            throw new BusinessException("该任务不存在", "ECAP0400");
        }
    }

    /**
     * 拾取(认领)任务，指定用户组里的人都可以拾取该任务 <br />
     *
     * @param processTaskReq 任务处理请求对象: {taskId, assignee}
     */
    public void claimTask(ProcessTaskReq processTaskReq) {
        // 待认领的任务ID
        String taskId = processTaskReq.getTaskId();
        // 任务认领人
        String assignee = processTaskReq.getAssignee();
        if (StringUtils.isBlank(taskId)) {
            throw new BusinessException("taskId 认领任务ID不能为空", "ECAP0001");
        }
        if (StringUtils.isBlank(assignee)) {
            throw new BusinessException("assignee 任务认领人不能为空", "ECAP0001");
        }
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        Optional.ofNullable(task).orElseThrow(() -> new BusinessException("任务不存在", "ECAP0400"));
        if (StringUtils.isNotBlank(task.getAssignee())) {
            throw new BusinessException("该任务已被人拾取", "ECAP0402");
        }
        List<String> groupIds = userCenterModel.listUserGroupsByUserId(assignee, processTaskReq.getChannel(),
            processTaskReq.getOperationUserId(), processTaskReq.getOrganId());
        task = taskService.createTaskQuery().taskId(taskId)
            // 根据候选人查询
            .taskCandidateUser(assignee).singleResult();
        if (groupIds != null && !groupIds.isEmpty()) {
            task = Optional.ofNullable(task).orElseGet(
                () -> taskService.createTaskQuery().taskId(taskId).taskCandidateGroupIn(groupIds).singleResult());
        }
        Optional.ofNullable(task).orElseThrow(() -> new BusinessException("该处理人不是候选人，无法领取该任务", "ECAP0410"));
        taskService.claim(taskId, assignee);
        IfpLogger.info("ProcessTaskModel.claimTask", "任务:{},由 {} 拾取成功", taskId, assignee);
    }

    /**
     * 归还(还原)任务，指拾取任务后的撤销操作 <br />
     *
     * @param processTaskReq 任务处理请求对象: {taskId, assignee}
     */
    public void restoreTask(ProcessTaskReq processTaskReq) {
        // 待还原的任务ID
        String taskId = processTaskReq.getTaskId();
        // 现在任务处理人
        String assignee = processTaskReq.getAssignee();
        if (StringUtils.isBlank(taskId)) {
            throw new BusinessException("taskId 待还原的任务ID不能为空", "ECAP0001");
        }
        if (StringUtils.isBlank(assignee)) {
            throw new BusinessException("assignee 任务处理人不能为空", "ECAP0001");
        }
        // 校验userId是否是taskId的负责人，如果是负责人才可以归还组任务
        // Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee(assignee).singleResult();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        Optional.ofNullable(task).orElseThrow(() -> new BusinessException("任务不存在", "ECAP0400"));
        if (StringUtils.isBlank(task.getAssignee())) {
            throw new BusinessException("请先拾取该任务", "ECAP0403");
        } else if (assignee.equals(task.getAssignee())) {
            // 取消任务有两种方式， 目前推荐使用 unclaim
            // 如果设置为null，归还组任务,该 任务没有负责人
            // taskService.setAssignee(taskId, null);
            taskService.unclaim(taskId);
            IfpLogger.info("ProcessTaskModel.restoreTask", "任务:{},由 {} 还原成功", taskId, assignee);
        } else {
            throw new BusinessException("任务处理人与原任务处理人不一致", "ECAP0401");
        }

    }

    /**
     * 查询任务详情(包括业务数据) <br />
     *
     * @param taskDetailReq 任务处理请求对象: {taskId}
     * @return 任务信息详情
     */
    public TaskDTO getTask(QueryTaskDetailReq taskDetailReq) {
        // 任务ID
        String taskId = taskDetailReq.getTaskId();
        if (StringUtils.isBlank(taskId)) {
            throw new BusinessException("taskId 任务ID不能为空", "ECAP0001");
        }
        HistoricTaskInstanceQuery historicTaskInstanceQuery =
            historyService.createHistoricTaskInstanceQuery().includeTaskLocalVariables().includeProcessVariables();
        HistoricTaskInstance historicTaskInstance = historicTaskInstanceQuery.taskId(taskId).singleResult();
        if (historicTaskInstance == null) {
            throw new BusinessException("该任务信息不存在", "ECAP0001");
        }
        TaskDTO taskDTO = dozerBeanMapper.map(historicTaskInstance, TaskDTO.class);
        // 整个流程批注记录
        List<Comment> processInstanceComments =
            taskService.getProcessInstanceComments(historicTaskInstance.getProcessInstanceId());
        // 当前任务批注
        List<Comment> taskComments = taskService.getTaskComments(taskId);
        List<CommentDTO> processInstanceCommentList =
            BeanUtil.mapList(dozerBeanMapper, processInstanceComments, CommentDTO.class);
        List<CommentDTO> taskCommentList = BeanUtil.mapList(dozerBeanMapper, taskComments, CommentDTO.class);
        taskDTO.setTaskComments(taskCommentList);
        taskDTO.setProcessComments(processInstanceCommentList);
        Map<String, Object> processVariables = historicTaskInstance.getProcessVariables();
        Map<String, Object> taskLocalVariables = historicTaskInstance.getTaskLocalVariables();
        /*List<HistoricVariableInstance> variableInstanceList =
            historyService.createHistoricVariableInstanceQuery().taskId(taskId).list();
        // 获取流程变量
        Map<String, Object> variables = variableInstanceList.stream()
            .collect(Collectors.toMap(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue));*/
        taskDTO.setVariables(taskLocalVariables);
        taskDTO.setProcessVariables(processVariables);

        if (taskLocalVariables != null && taskLocalVariables.size() > 0) {
            // 操作结果
            if (taskLocalVariables.get(WorkflowConstants.USER_TASK_APPROVE_RESULT) != null) {
                taskDTO.setApproveResult(
                    String.valueOf(taskLocalVariables.get(WorkflowConstants.USER_TASK_APPROVE_RESULT)));
            }
            String votingNumber = (String)taskLocalVariables.get(VotingVariables.VOTING_FLAG);
            if (StringUtils.isNotBlank(votingNumber)) {
                // 会签号
                taskDTO.setCounterSignNumber(votingNumber);
                taskDTO.setCountersign(true);
            } else {
                taskDTO.setCountersign(false);
            }
        }

        if (processVariables != null && processVariables.size() > 0) {
            // 发起人ID
            taskDTO.setStartUserId((String)processVariables.get(WorkflowConstants.START_USER_ID));
            // 发起渠道
            taskDTO.setStartChannelSignal((String)processVariables.get(WorkflowConstants.CHANNEL_SIGNAL));
            // 发起时间
            taskDTO.setStartTime(String.valueOf(processVariables.get(WorkflowConstants.FLOW_START_TIME)));
            // 发起业态
            taskDTO.setStartFormatSignal((String)processVariables.get(WorkflowConstants.FORMAT_SIGNAL));
        }

        // 封装业务动作参数集合
        setBusinessOperateData(taskDTO, taskLocalVariables);
        FlowNode taskFlowNode =
            getFlowNode(historicTaskInstance.getProcessDefinitionId(), historicTaskInstance.getTaskDefinitionKey());
        UserTask userTaskDefinition;
        if (taskFlowNode instanceof UserTask) {
            userTaskDefinition = (UserTask)taskFlowNode;
            taskDTO.setCandidateUsers(userTaskDefinition.getCandidateUsers());
            taskDTO.setCandidateGroups(userTaskDefinition.getCandidateGroups());
            taskDTO.setCountersign(userTaskDefinition.hasMultiInstanceLoopCharacteristics());
        }
        return taskDTO;
    }

    /**
     * 查询用户待审批任务列表（流程明确指定的个人任务，不包含候选用户、组）
     *
     * @param startUserId 流程发起人
     * @return 待审批任务列表
     */
    public List<TaskDTO> listAuditingTaskByUserId(String startUserId) {
        TaskQuery taskQuery = taskService.createTaskQuery();
        taskQuery.taskAssignee(startUserId);
        taskQuery.orderByTaskCreateTime().desc();
        List<Task> tasks = taskQuery.list();
        return BeanUtil.mapList(dozerBeanMapper, tasks, TaskDTO.class);
    }

    /**
     * 审核任务、完成任务
     *
     * @param completeTaskReq 完成任务请求对象
     */
    @Transactional(rollbackFor = Exception.class)
    public void completeTask(CompleteTaskReq completeTaskReq) {
        String taskId = completeTaskReq.getTaskId();
        String assignee = completeTaskReq.getAssignee();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        Optional.ofNullable(task).orElseThrow(() -> new BusinessException("该任务信息不存在", "ECAP0400"));
        Optional.ofNullable(task.getAssignee()).filter(StringUtils::isNotBlank)
            .orElseThrow(() -> new BusinessException("请先拾取该任务", "ECAP0403"));
        if (!assignee.equals(task.getAssignee())) {
            throw new BusinessException("当前审批人与任务处理人不一致", "ECAP0401");
        }
        // 查询下一个审批节点信息，判断是否需要手动设置审批人
        NextTaskDTO nextUserTask = getNextUserTask(taskId);
        if (nextUserTask != null) {
            String nextAssignee = nextUserTask.getAssignee();
            List<String> nextCandidateUsers = nextUserTask.getCandidateUsers();
            List<String> nextCandidateGroups = nextUserTask.getCandidateGroups();
            boolean usersFlag = (nextCandidateUsers == null || nextCandidateUsers.isEmpty());
            boolean groupsFlag = (nextCandidateGroups == null || nextCandidateGroups.isEmpty());
            if (StringUtils.isBlank(nextAssignee) && usersFlag && groupsFlag
                && StringUtils.isBlank(completeTaskReq.getNextAssignee())) {
                throw new BusinessException("下一节点审批人不能为空，需要手动指定", "ECAP0413");
            }
        }
        String comment = completeTaskReq.getComment();
        // 添加当前任务的办理人
        Authentication.setAuthenticatedUserId(assignee);
        // 添加备注
        String commentId = null;
        if (StringUtils.isNotBlank(comment)) {
            commentId = taskService.addComment(taskId, task.getProcessInstanceId(), comment).getId();
        }
        Map<String, Object> variables = completeTaskReq.getVariables();
        variables = Optional.ofNullable(variables).orElse(Maps.newHashMap());
        variables.put(WorkflowConstants.USER_TASK_APPROVE_RESULT, completeTaskReq.getApproveResult());
        // 存储到local变量中
        taskService.setVariablesLocal(taskId, variables);
        // 获取流程自定义变量, 如果不为空，则添加到流程变量中
        Map<String, Object> processVariables = completeTaskReq.getProcessVariables();
        // processVariables.put(WorkflowConstants.USER_TASK_APPROVE_RESULT, completeTaskReq.getApproveResult());

        // add by liwq on 2021-04-28 start
        // 将业务数据、流程摘要、发起人等字段放到流程变量里。
        if (processVariables == null || processVariables.size() == 0) {
            processVariables = new HashMap<String, Object>();
        }
        // 业务数据
        List<BusinessModuleData> businessModuleData = completeTaskReq.getBusinessData();
        // 若是传空数组过来，就认为是要赋值空数组给变量池
        if (businessModuleData != null && businessModuleData.size() == 0) {
            processVariables.put(WorkflowConstants.FLOW_BUSINESS_DATA, completeTaskReq.getBusinessData());
        }
        // 若是传null过来，就不做处理。不为null并且数组大于0，校验之后，才放入变量池
        if (businessModuleData != null && businessModuleData.size() > 0) {
            processRuntimeModel.validBusinessData(businessModuleData);
            List<BusinessModuleData> sortList = processRuntimeModel.sortBusinessData(businessModuleData);
            processVariables.put(WorkflowConstants.FLOW_BUSINESS_DATA, sortList);
        }
        // 流程摘要
        if (!StringUtils.isEmpty(completeTaskReq.getProcessRemark())) {
            processVariables.put(WorkflowConstants.FLOW_BUSINESS_SUMMARY, completeTaskReq.getProcessRemark());
        }
        // 金控经办人员
        if (!StringUtils.isEmpty(completeTaskReq.getJinKongHandling())) {
            processVariables.put(WorkflowConstants.FLOW_BUSINESS_QDPF_OPERATOR, completeTaskReq.getJinKongHandling());
            // 金控经办人员姓名
            String qdpfOperatorName = null;
            UserBaseInformationDto base = null;
            List<UserInfoDto> userInfo =
                userCenterModel.queryUserInfo(String.valueOf(completeTaskReq.getJinKongHandling()),
                    completeTaskReq.getChannel(), String.valueOf(completeTaskReq.getOperationUserId()));
            if (userInfo != null && userInfo.size() > 0) {
                base = userInfo.get(0).getUserBaseInformationDto();
                if (base == null || !"1".equals(base.getUserType())) {
                    throw new BusinessException("金控经办人员不存在", "ECAP0509");
                }
                qdpfOperatorName = base.getUserName();
                processVariables.put(WorkflowConstants.FLOW_BUSINESS_QDPF_OPERATOR_NAME, qdpfOperatorName);
                // 金控经办人员钉钉ID
                if (StringUtils.isNotEmpty(base.getDingtalkUserid())) {
                    processVariables.put(WorkflowConstants.FLOW_BUSINESS_QDPF_DINGTALK_USERID,
                        base.getDingtalkUserid());
                }
            } else {
                throw new BusinessException("金控经办人员不存在", "ECAP0509");
            }
        }

        // 业务场景
        if (!StringUtils.isEmpty(completeTaskReq.getBusinessScene())) {
            processVariables.put(WorkflowConstants.FLOW_BUSINESS_SCENE, completeTaskReq.getBusinessScene());
        }
        // add by liwq on 2021-04-28 End

        // add By liwq on 2021-1-25 Start
        // 跨渠道审批相关：在审批时，可以更改“可以处理该流程实例的渠道标志”字段
        if (!StringUtils.isEmpty(completeTaskReq.getFlowCanOperateChannelSignal())) {
            Boolean channelFlag = CommUtils.checkChannel(completeTaskReq.getFlowCanOperateChannelSignal());
            if (!channelFlag) {
                throw new BusinessException("可以处理该流程实例的渠道标志包含不合规则字符，请检查", "ECAP0205");
            }
            if (processVariables == null || processVariables.size() == 0) {
                processVariables = new HashMap<String, Object>();
            }
            processVariables.put(WorkflowConstants.FLOW_CAN_OPERATE_CHANNEL_SIGNAL,
                completeTaskReq.getFlowCanOperateChannelSignal());
        }
        // add By liwq on 2021-1-25 End

        // add by syf on 2023-03-08 start 新增可修改公司名称和产品名称
        // 公司名称
        if (StringUtils.isNotEmpty(completeTaskReq.getCompanyName())) {
            processVariables.put(WorkflowConstants.FLOW_COMPANY_NAME, completeTaskReq.getCompanyName());
        }
        // 产品名称
        if (StringUtils.isNotEmpty(completeTaskReq.getProductName())) {
            processVariables.put(WorkflowConstants.FLOW_PRODUCT_NAME, completeTaskReq.getProductName());
        }
        // 添加审核附件信息列表
        // 只有添加了备注时才记录
        if (StringUtils.isNotBlank(commentId)) {
            if (completeTaskReq.getCommentFiles() != null && completeTaskReq.getCommentFiles().size() > 0) {
                ArCommentExt arCommentExt = new ArCommentExt();
                arCommentExt.setOperateType(String.valueOf(completeTaskReq.getApproveResult()));
                if (StringUtils.isNotBlank(completeTaskReq.getAssignee())) {
                    Integer assigneeInt = userCenterModel.getUserId(completeTaskReq.getAssignee());
                    arCommentExt.setAssignee(assigneeInt);
                    UserBaseInformationDto base = null;
                    List<UserInfoDto> userInfo = userCenterModel.queryUserInfo(completeTaskReq.getAssignee(),
                        completeTaskReq.getChannel(), String.valueOf(completeTaskReq.getOperationUserId()));
                    if (userInfo != null && userInfo.size() > 0) {
                        base = userInfo.get(0).getUserBaseInformationDto();
                        if (base == null) {
                            throw new BusinessException("任务处理人不存在", "ECAP0509");
                        }
                        arCommentExt.setAssigneeName(base.getUserName());
                    }
                }
                List<CommentFile> commentFiles = completeTaskReq.getCommentFiles();
                arCommentExt.setFiles(commentFiles != null ? JSON.toJSONString(commentFiles) : null);
                arCommentExt.setCreateBy(completeTaskReq.getOperationUserId());
                arCommentExt.setGmtCreate(LocalDateTime.now());
                arCommentExt.setModifiedBy(completeTaskReq.getOperationUserId());
                arCommentExt.setGmtModified(LocalDateTime.now());
                arCommentExt.setCommentId(commentId);
                arCommentExt.setTaskId(task.getId());
                arCommentExt.setProcInstId(task.getProcessInstanceId());
                arCommentExtMapper.insert(arCommentExt);
            }
        }
        // if(StringUtils.isNotBlank(completeTaskReq.getAnnexList())){
        // taskService.addComment(taskId, task.getProcessInstanceId(), FILES_TYPE,
        // completeTaskReq.getAnnexList());
        // }
        // add by syf on 2023-03-08 end
        try {
            if (processVariables != null && !processVariables.isEmpty()) {
                taskService.complete(taskId, processVariables);
            } else {
                // 完成任务
                taskService.complete(taskId);
            }
        } catch (ActivitiException e) {
            throw new BusinessException("流程配置异常:" + e.getMessage(), "ECAP0004");
        }

        // 设置下一节点审批人
        if (StringUtils.isNotBlank(completeTaskReq.getNextAssignee())) {
            // 查询需要设置审批人的任务，可能是多个
            List<Task> nextTaskList =
                taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).active().list();
            if (nextTaskList == null || nextTaskList.isEmpty()) {
                throw new BusinessException("下一节点信息不存在，无法设置下一节点审批人", "ECAP0414");
            }
            if (nextTaskList.size() > 1) {
                throw new BusinessException("下一节点信息为并行任务，无法设置下一节点审批人", "ECAP0415");
            }
            Task nextTask = nextTaskList.get(0);
            // TODO: 是否需要支持指定多个角色和多个人
            taskService.setAssignee(nextTask.getId(), completeTaskReq.getNextAssignee());
        }

    }

    /**
     * 完成第一个任务，用于开始流程时
     *
     * @param startProcessInstanceReq 启动流程时请求对象信息
     * @param task 流程第一个任务信息
     */
    public void completeFirstTask(StartProcessInstanceReq startProcessInstanceReq, Task task) {
        CompleteTaskReq completeTaskReq = new CompleteTaskReq();
        completeTaskReq.setTaskId(task.getId());
        completeTaskReq.setAssignee(startProcessInstanceReq.getStartUserId());
        // update by liwq on 2020-12-14 start
        // 发起流程增加“意见备注”字段
        if (!StringUtils.isEmpty(startProcessInstanceReq.getComment())) {
            completeTaskReq.setComment(startProcessInstanceReq.getComment());
        }
        if (StringUtils.isEmpty(startProcessInstanceReq.getComment())) {
            completeTaskReq.setComment(OperatingTypeEnum.APPLY.getDesc());
        }
        // update by liwq on 2020-12-14 end
        completeTaskReq.setApproveResult(OperatingTypeEnum.APPLY.getCode());
        completeTaskReq.setVariables(startProcessInstanceReq.getVariables());
        completeTaskReq.setTraceId(startProcessInstanceReq.getTraceId());
        completeTaskReq.setChannel(startProcessInstanceReq.getChannel());
        completeTaskReq.setOperationUserId(startProcessInstanceReq.getOperationUserId());
        completeTaskReq.setRequestTime(startProcessInstanceReq.getRequestTime());
        completeTask(completeTaskReq);
    }

    /**
     * 流程节点跳转
     *
     * @param currentTaskId 当前任务节点ID
     * @param targetTaskKeyId 目标任务节点ID
     * @param currentUser 当前任务节点处理人(非必填，一旦填写会校验处理人是否一致)
     * @param approveResult 跳转意图
     * @param reason 备注
     */
    @Transactional(rollbackFor = Exception.class)
    public void nodeJump(String currentTaskId, String targetTaskKeyId, String currentUser, String reason,
        OperatingTypeEnum operatingTypeEnum, Integer approveResult, Map<String, Object> variables,
        Map<String, Object> processVariables, ArCommentExt arCommentExt) {
        // 当前任务
        Task currentTask = taskService.createTaskQuery().taskId(currentTaskId).singleResult();

        if (null == currentTask) {
            throw new BusinessException("无效任务ID [" + currentTaskId + "]", "ECAP0400");
        }
        // add by liwq on 2021-03-21 start
        // 添加审批结果,跳转意图
        variables = Optional.ofNullable(variables).orElse(Maps.newHashMap());
        variables.put(WorkflowConstants.USER_TASK_APPROVE_RESULT, approveResult);
        // 存储到local变量中
        taskService.setVariablesLocal(currentTask.getId(), variables);
        String commentId = null;
        if (!StringUtils.isEmpty(reason)) {
            commentId = taskService.addComment(currentTask.getId(), currentTask.getProcessInstanceId(), reason).getId();
        }
        if (StringUtils.isEmpty(reason)) {
            commentId = taskService
                .addComment(currentTask.getId(), currentTask.getProcessInstanceId(), operatingTypeEnum.getDesc())
                .getId();
        }
        // add by liwq on 2021-03-21 end

        // 如果传递 currentUser 当前任务处理人字段，则执行当前处理人与任务处理人是否一致的校验
        if (StringUtils.isNotBlank(currentUser)) {
            String taskAssignee = currentTask.getAssignee();
            if (StringUtils.isBlank(taskAssignee) || !currentUser.equals(currentTask.getAssignee())) {
                throw new BusinessException("该任务非当前用户提交，无法操作", "ECAP0404");
            }
        }
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
            .processInstanceId(currentTask.getProcessInstanceId()).singleResult();
        if (null == processInstance) {
            throw new BusinessException("该任务对应流程已完成，无法回退", "ECAP0405");
        }
        // 获取流程定义
        // Process process = repositoryService.getBpmnModel(currentTask.getProcessDefinitionId()).getMainProcess();
        ExecutionQuery executionQuery = runtimeService.createExecutionQuery();
        ExecutionEntity executionEntity =
            (ExecutionEntity)executionQuery.executionId(currentTask.getExecutionId()).singleResult();
        String activityId = executionEntity.getActivityId();
        // 获取当前活动节点信息
        // FlowNode currentFlowNode = (FlowNode)process.getFlowElement(activityId);
        FlowNode currentFlowNode = getFlowNode(currentTask.getProcessDefinitionId(), activityId);
        if (currentFlowNode.getBehavior() instanceof MultiInstanceActivityBehavior) {
            throw new BusinessException("当前任务为会签节点，无法使用该服务回退", "ECAP0406");
        }
        // 获取目标节点定义
        // FlowNode targetNode = (FlowNode)process.getFlowElement(targetTaskKeyId);
        FlowNode targetNode = getFlowNode(currentTask.getProcessDefinitionId(), targetTaskKeyId);
        Optional.ofNullable(targetNode).orElseThrow(() -> new BusinessException("找不到目标节点信息", "ECAP0411"));
        // 删除当前运行任务
        String executionEntityId = managementService.executeCommand(new DeleteTaskCmd(currentTask.getId(), reason));
        // 流程执行到来源节点
        managementService.executeCommand(new SetFlowNodeAndGoCmd(targetNode, executionEntityId));

        // 记录从哪个节点退回/驳回
        // 查询退回后，目前的活动节点
        Task targetTask = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId())
            .active().includeProcessVariables().includeTaskLocalVariables().singleResult();
        // 记录从哪里退回/驳回
        taskService.setVariableLocal(targetTask.getId(), WorkflowConstants.FLOW_FROM_TASK_DEFINITION_KEY,
            currentTask.getTaskDefinitionKey());
        // 记录到该节点时的操作
        taskService.setVariableLocal(targetTask.getId(), WorkflowConstants.FLOW_FROM_TASK_OPERATE,
            operatingTypeEnum.getCode());
        if (processVariables != null && processVariables.size() > 0) {
            taskService.setVariables(targetTask.getId(), processVariables);
        }
        // add by syf on 2023-03-08 start
        // 记录审核附件信息列表
        if (StringUtils.isNotBlank(arCommentExt.getFiles())) {
            arCommentExt.setCommentId(commentId);
            arCommentExt.setTaskId(currentTask.getId());
            arCommentExt.setProcInstId(currentTask.getProcessInstanceId());
            arCommentExtMapper.insert(arCommentExt);
        }
        // add by syf on 2023-03-08 end
    }

    /**
     * 获取节点信息
     *
     * @param processDefinitionId 流程定义ID
     * @param activityId 节点ID
     * @return 节点对象信息
     */
    private FlowNode getFlowNode(String processDefinitionId, String activityId) {
        Process mainProcess = repositoryService.getBpmnModel(processDefinitionId).getMainProcess();
        return (FlowNode)mainProcess.getFlowElement(activityId);
    }

    /**
     * 退回到第一个任务节点, 即退回至发起人
     *
     * @param currentTaskId 当前任务节点ID
     * @param currentUser 当前任务节点处理人(非必填，一旦填写会校验处理人是否一致)
     */
    public void nodeJumpToFirstTask(String currentTaskId, String currentUser, String reason,
        Map<String, Object> variables, Map<String, Object> processVariables, ArCommentExt arCommentExt) {
        nodeJumpToUserTask("first", currentTaskId, currentUser,
            Optional.ofNullable(reason).orElse(OperatingTypeEnum.RETURN.getDesc()), variables, processVariables,
            arCommentExt);
    }

    /**
     * 退回到上一个节点
     *
     * @param currentTaskId 当前任务节点ID
     * @param currentUser 当前任务节点处理人(非必填，一旦填写会校验处理人是否一致)
     */
    public void nodeJumpToPrevTask(String currentTaskId, String currentUser, String reason,
        Map<String, Object> variables) {
        nodeJumpToUserTask("prev", currentTaskId, currentUser,
            Optional.ofNullable(reason).orElse(OperatingTypeEnum.REJECT.getDesc()), variables, null, null);
    }

    /**
     * 撤回任务
     *
     * @param withDrawTaskReq
     */
    public void withDrawTask(WithDrawTaskReq withDrawTaskReq) {
        // 撤回有两种情况；①撤回到发起节点，②撤回到当前节点的上一个节点
        // Step1: 取出目标节点的keyId
        String targetTaskKeyId = validAndGetTargetTaskKeyId(withDrawTaskReq);

        // Step2: 跳转到目标节点
        if (StringUtils.isEmpty(targetTaskKeyId)) {
            throw new BusinessException("服务异常，请联系系统管理员", "ECAP0000");
        }
        if (!StringUtils.isEmpty(targetTaskKeyId)) {
            nodeJump(withDrawTaskReq.getCurrentTaskId(), targetTaskKeyId, null, withDrawTaskReq.getReason(),
                OperatingTypeEnum.CANCLE, OperatingTypeEnum.CANCLE.getCode(), withDrawTaskReq.getVariables(), null,
                null);
        }

    }

    /**
     * 执行撤回任务前的校验，检验通过后取出targetTaskKeyId返回
     *
     * @param withDrawTaskReq
     * @return
     */
    public String validAndGetTargetTaskKeyId(WithDrawTaskReq withDrawTaskReq) {
        // Step1: 根据taskID找出当前任务，并校验。
        Task currentTask = taskService.createTaskQuery().taskId(withDrawTaskReq.getCurrentTaskId()).singleResult();
        // 校验任务是否存在
        if (null == currentTask) {
            throw new BusinessException("无效任务ID [" + withDrawTaskReq.getCurrentTaskId() + "]", "ECAP0400");
        }

        // Step2: 找出目标节点（流程发起节点，或者当前节点的上一个节点）
        HistoricActivityInstanceQuery historicActivityInstanceQuery =
            historyService.createHistoricActivityInstanceQuery().processInstanceId(currentTask.getProcessInstanceId())
                .orderByHistoricActivityInstanceEndTime().activityType("userTask").finished();
        // 获取流程发起节点
        if (withDrawTaskReq.isWithDrawStartNodeFlag()) {
            historicActivityInstanceQuery.asc();
        }
        // 获取当前节点的上一个节点
        if (!withDrawTaskReq.isWithDrawStartNodeFlag()) {
            historicActivityInstanceQuery.desc();
        }
        // 校验节点是否存在
        List<HistoricActivityInstance> activityInstances = historicActivityInstanceQuery.list();
        Optional<HistoricActivityInstance> targetTask = activityInstances.stream().findFirst();
        if (!targetTask.isPresent()) {
            throw new BusinessException("没有可撤回的任务节点", "ECAP0408");
        }
        // 流程撤回到发起人的话，校验当前操作人是不是任务发起人
        if (withDrawTaskReq.isWithDrawStartNodeFlag()) {
            String firstAssine = activityInstances.get(0).getAssignee();
            if (!String.valueOf(withDrawTaskReq.getOperationUserId()).equals(firstAssine)) {
                throw new BusinessException("当前操作人不是任务发起人，无权撤回到任务开始节点", "ECAP0508");
            }
        }

        // Step3: 取出目标节点的keyId
        String targetTaskKeyId = targetTask.get().getActivityId();
        // 流程退回到上一节点的话，校验三点。
        if (!withDrawTaskReq.isWithDrawStartNodeFlag()) {
            // 校验①：校验当前节点是不是会签
            Boolean currentTaskFlag = isCounterSignAccordTaskInfo(currentTask);
            if (currentTaskFlag) {
                throw new BusinessException("当前任务为会签节点，无法使用该服务回退", "ECAP0406");
            }

            // 校验②：校验上一节点是否是会签，会签的话不允许退回
            String processDefinitionId = targetTask.get().getProcessDefinitionId();
            boolean prevTaskFlag = isCounterSign(processDefinitionId, targetTaskKeyId);
            if (prevTaskFlag) {
                throw new BusinessException("跳转的目标节点为会签节点，无法使用该服务回退", "ECAP0606");
            }

            // 校验③：校验上传的操作人是不是上一节点操作人
            String prevAssine = activityInstances.get(0).getAssignee();
            if (!String.valueOf(withDrawTaskReq.getOperationUserId()).equals(prevAssine)) {
                throw new BusinessException("请上传正确的操作人：" + prevAssine, "ECAP0509");
            }
        }
        return targetTaskKeyId;
    }

    /**
     * 根据当前task判断当前节点是否会签
     * 
     * @param currentTask
     * @return
     */
    public Boolean isCounterSignAccordTaskInfo(Task currentTask) {
        ExecutionQuery executionQuery = runtimeService.createExecutionQuery();
        ExecutionEntity executionEntity =
            (ExecutionEntity)executionQuery.executionId(currentTask.getExecutionId()).singleResult();
        String activityId = executionEntity.getActivityId();
        // 获取当前活动节点信息
        FlowNode currentFlowNode = getFlowNode(currentTask.getProcessDefinitionId(), activityId);
        if (currentFlowNode.getBehavior() instanceof MultiInstanceActivityBehavior) {
            return true;
        }
        return false;
    }

    /**
     * 根据processDefinitionId + taskDefinitionKey判断当前节点是否为会签
     *
     * @param processDefinitionId
     * @param taskDefinitionKey
     * @return true是；false否
     */
    private Boolean isCounterSign(String processDefinitionId, String taskDefinitionKey) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        Process process = bpmnModel.getProcesses().get(0);
        UserTask userTask = (UserTask)process.getFlowElement(taskDefinitionKey);
        if (userTask.getLoopCharacteristics() == null) {
            return false;
        }
        return true;
    }

    /**
     * 退回、驳回
     *
     * @param type first: 发起人, prev: 上一个任务
     * @param currentTaskId 当前任务ID
     * @param currentUser 前任务节点处理人(非必填，一旦填写会校验处理人是否一致)
     * @param reason 备注
     */
    public void nodeJumpToUserTask(String type, String currentTaskId, String currentUser, String reason,
        Map<String, Object> variables, Map<String, Object> processVariables, ArCommentExt arCommentExt) {
        Task currentTask = taskService.createTaskQuery().taskId(currentTaskId).singleResult();
        Integer approveResult = 0;
        if (null == currentTask) {
            throw new BusinessException("无效任务ID [" + currentTaskId + "]", "ECAP0400");
        }
        OperatingTypeEnum operatingTypeEnum;
        HistoricActivityInstanceQuery historicActivityInstanceQuery =
            historyService.createHistoricActivityInstanceQuery().processInstanceId(currentTask.getProcessInstanceId())
                .orderByHistoricActivityInstanceEndTime().activityType("userTask").finished();
        if ("first".equals(type)) {
            // 查询历史中首任务节点
            historicActivityInstanceQuery.asc();
            operatingTypeEnum = OperatingTypeEnum.RETURN;
            approveResult = OperatingTypeEnum.RETURN.getCode();
        } else if ("prev".equals(type)) {
            // 查询历史中上一任务节点
            historicActivityInstanceQuery.desc();
            operatingTypeEnum = OperatingTypeEnum.REJECT;
            approveResult = OperatingTypeEnum.REJECT.getCode();
        } else {
            throw new BusinessException("检查[type]类型", "ECAP0407");
        }
        List<HistoricActivityInstance> activityInstances = historicActivityInstanceQuery.list();
        Optional<HistoricActivityInstance> nextTask = activityInstances.stream()
            // .filter(activityInstance -> "userTask".equals(activityInstance.getActivityType()))
            .findFirst();
        if (!nextTask.isPresent()) {
            throw new BusinessException("没有可撤回的任务节点", "ECAP0408");
        }
        String targetTaskId = nextTask.get().getActivityId();
        nodeJump(currentTaskId, targetTaskId, currentUser, reason, operatingTypeEnum, approveResult, variables,
            processVariables, arCommentExt);
    }

    /**
     * 根据任务ID判断任务是否存在并运行
     *
     * @param taskId 任务ID
     * @return 任务对象
     */
    public Task getTaskInfo(String taskId) {
        Task currentTask = taskService.createTaskQuery().taskId(taskId).includeProcessVariables()
            .includeTaskLocalVariables().singleResult();
        if (null == currentTask) {
            throw new BusinessException("无效任务ID [" + taskId + "]", "ECAP0400");
        }
        return currentTask;
    }

    /**
     * 委派任务
     *
     * @param req 委派任务请求对象
     */
    public void delegateTask(TransferTaskReq req) {
        Task task = getTaskInfo(req.getTaskId());
        if (StringUtils.isBlank(task.getAssignee())) {
            throw new BusinessException("请先拾取该任务", "ECAP0403");
        }
        if (StringUtils.isNotBlank(req.getAssignee()) && !req.getAssignee().equals(task.getAssignee())) {
            throw new BusinessException("任务处理人与原任务处理人不一致", "ECAP0401");
        }
        taskService.delegateTask(req.getTaskId(), req.getReceiver());
    }

    /**
     * 委派任务归还
     *
     * @param req 委派任务归还请求对象
     */
    public void resolveTask(ResolveTaskReq req) {
        Task task = getTaskInfo(req.getTaskId());
        if (StringUtils.isBlank(task.getAssignee())) {
            throw new BusinessException("请先拾取该任务", "ECAP0403");
        }
        if (StringUtils.isNotBlank(req.getAssignee()) && !req.getAssignee().equals(task.getAssignee())) {
            throw new BusinessException("任务处理人与原任务处理人不一致", "ECAP0401");
        }
        taskService.resolveTask(req.getTaskId(), req.getVariables());
    }

    /**
     * 根据当前任务 id 查询下一个任务节点信息
     *
     * @param taskId 当前任务 id
     * @return 下一个任务节点
     */
    public NextTaskDTO getNextUserTask(String taskId) {
        Task task = getTaskInfo(taskId);
        ProcessInstance processInstance =
            runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
        if (null == processInstance) {
            throw new BusinessException("该任务对应流程已完成，无法回退", "ECAP0405");
        }
        // 每个流程实例只有一个executionId
        // 获得流程定义
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(task.getProcessDefinitionId());

        // 获得当前流程的活动ID
        ExecutionQuery executionQuery = runtimeService.createExecutionQuery();
        Execution execution = executionQuery.executionId(task.getExecutionId()).singleResult();
        String activityId = execution.getActivityId();
        UserTask userTask;
        while (true) {
            // 根据活动节点获取当前的组件信息
            FlowNode flowNode = getFlowNode(processDefinition.getId(), activityId);
            if (flowNode == null) {
                return null;
            }
            // 获取该流程组件的之后/之前的组件信息
            List<SequenceFlow> sequenceFlowListOutGoing = flowNode.getOutgoingFlows();
            // List<SequenceFlow> sequenceFlowListIncoming=flowNode.getIncomingFlows();
            // 获取的下个节点不一定是userTask的任务节点，所以要判断是否是任务节点
            // sequenceFlowListOutGoing数量可能大于1,可以自己做判断,此处只取第一个
            if (sequenceFlowListOutGoing == null || sequenceFlowListOutGoing.isEmpty()) {
                return null;
            }
            FlowElement flowElement = sequenceFlowListOutGoing.get(0).getTargetFlowElement();
            if (flowElement instanceof UserTask) {
                userTask = (UserTask)flowElement;
                break;
            } else {
                // 下一节点不是任务userTask的任务节点,所以要获取再下一个节点的信息,直到获取到userTask任务节点信息
                String flowElementId = flowElement.getId();
                activityId = flowElementId;
                continue;
            }
        }
        NextTaskDTO nextTaskDTO = dozerBeanMapper.map(userTask, NextTaskDTO.class);
        nextTaskDTO.setCountersign(userTask.hasMultiInstanceLoopCharacteristics());
        return nextTaskDTO;
    }

    /**
     * 根据任务 id 和用户 id 查询用户是否有处理该任务的处理权限
     *
     * @param processTaskReq 任务处理请求对象
     */
    public boolean checkTaskPermission(ProcessTaskReq processTaskReq) {
        // 任务处理人
        String assignee = processTaskReq.getAssignee();
        // 待查询的任务ID
        String taskId = processTaskReq.getTaskId();

        if (StringUtils.isBlank(assignee)) {
            throw new BusinessException("assignee 任务处理人不能为空", "ECAP0001");
        }

        if (StringUtils.isBlank(taskId)) {
            throw new BusinessException("任务ID不能为空", "ECAP0001");
        }
        IfpLogger.info("ProcessTaskModel.checkTaskPermission", "查询用户: {}, 是否有权限处理任务:{}", assignee, taskId);
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        Optional.ofNullable(task).orElseThrow(() -> new BusinessException("任务不存在", "ECAP0400"));
        if (StringUtils.isNotBlank(task.getAssignee())) {
            return assignee.equals(task.getAssignee());
        }
        List<String> groupIds = userCenterModel.listUserGroupsByUserId(assignee, processTaskReq.getChannel(),
            processTaskReq.getOperationUserId(), processTaskReq.getOrganId());
        task = taskService.createTaskQuery().taskId(taskId)
            // 根据候选人查询
            .taskCandidateUser(assignee).singleResult();
        if (groupIds != null && !groupIds.isEmpty()) {
            task = Optional.ofNullable(task).orElseGet(
                () -> taskService.createTaskQuery().taskId(taskId).taskCandidateGroupIn(groupIds).singleResult());
        }
        // Optional.ofNullable(task).orElseThrow(() -> new BusinessException("处理人无权处理该任务", "ECAP0416"));
        return task != null;
    }

    /**
     * 封装业务动作参数集合 <br>
     * 改造之前，业务动作参数以参数名为变量名均存放至任务变量池当中 <br>
     * 现在调整，不仅任务变量池当中存在，并将所有业务动作参数集合单独存放于任务变量池中指定的变量中 <br>
     * 
     * <pre>
     *   为了兼容改造之前的处理，故需要先从指定变量中获取，再从任务变量池当中将所有业务动作参数取出，再存放至单独变量中;
     *   同名的即可覆盖；
     *   后期可优化，直接从指定变量中获取；
     * </pre>
     * 
     * @param taskDTO 返回的任务信息传输对象
     * @param taskLocalVariables 任务范围的变量池
     */
    protected void setBusinessOperateData(TaskDTO taskDTO, Map<String, Object> taskLocalVariables) {
        if (taskLocalVariables != null) {
            Object businessOperation = taskLocalVariables.get(WorkflowConstants.FLOW_BUSINESS_OPERATE_DATA);
            Map<String, Object> businessOperationMap =
                (businessOperation != null) ? (Map<String, Object>)businessOperation : new HashMap<>(16);

            // 查询现所有可配置的动作参数，并判断任务变量池当中是否已存在该参数，若有则放入业务动作集合中
            List<TaskAttrDTO> listTaskAttr = taskAttrModel.listTaskAttr();
            if (CollectionUtils.isNotEmpty(listTaskAttr)) {
                for (TaskAttrDTO t : listTaskAttr) {
                    if (taskLocalVariables.containsKey(t.getCode())) {
                        businessOperationMap.put(t.getCode(), taskLocalVariables.get(t.getCode()));
                    }
                }
            }
            taskDTO.setBusinessOperateVariables(businessOperationMap);
        }
    }

    /**
     * 发送钉钉通知，具体由移动审批判断是否需要发送
     * 
     * @param taskId 当前任务 id
     * @param userId 当前用户
     */
    public void sendDingtalkNotification(String taskId, String userId) {
        // 当前任务
        HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
        if (null == task) {
            IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(), "审批中心调用移动审批推送钉钉消息服务异常，任务 {} 不存在",
                taskId);
            return;
        }
        mobileApproveModel.sendDingtalkNotification(task.getProcessInstanceId(), taskId, userId);
    }

}
